package com.benshou.bsoa.common.util;

import jakarta.validation.constraints.NotNull;
import lombok.NonNull;
import org.springframework.cglib.beans.BeanCopier;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;

/**
 * 基于 Cglib 框架的属性拷贝工具
 *
 * <p>
 *     本工具类提供了高性能的 Java 对象属性拷贝功能，内部使用 Spring 的 Cglib BeanCopier 实现，
 *     并通过缓存 BeanCopier 实例来提高性能。适用于同名属性之间的拷贝。
 * </p>
 * <p>
 *     注意：本工具有如下受限：
 *     1. 不支持类型转换，需提前确保字段类型一致
 *     2. 默认不调用目标对象构造函数（浅拷贝）
 * </p>
 * @author <a href="mailto:liuzhenbang@benshou.com">Liu Zhenbang</a>
 */
public class CglibCopy {

    /**
     * BeanCopier 缓存，用于存储已创建的BeanCopier实例，提高性能
     */
    private static final Map<_CacheKey, BeanCopier> CACHE = new ConcurrentHashMap<>();

    
    /**
     * 将源对象的属性拷贝到目标对象
     *
     * @param from 源对象
     * @param fromClass 源对象类型，如果为null则自动获取
     * @param to 目标对象
     * @param toClass 目标对象类型，如果为null则自动获取
     * @param <F> 源对象类型
     * @param <T> 目标对象类型
     */
    public static <F, T> void copy(F from, Class<? extends F> fromClass, T to, Class<? super T> toClass) {
        if (from == null || to == null) {
            return;
        }

        if (fromClass == null) {
            @SuppressWarnings("unchecked")
            Class<F> cls = (Class<F>) from.getClass();
            fromClass = cls;
        }

        if (toClass == null) {
            @SuppressWarnings("unchecked")
            Class<T> cls = (Class<T>) to.getClass();
            toClass = cls;
        }

        var copier = getOrCreateBeanCopier(fromClass, toClass);
        copier.copy(from, to, null);
    }

    /**
     * 将源对象的属性拷贝到目标对象（简化版本）
     *
     * @param from 源对象
     * @param to 目标对象
     */
    public static void copy(Object from, Object to) {
        copy(from, null, to, null);
    }

    /**
     * 创建目标对象实例并将源对象属性拷贝到其中
     *
     * @param from 源对象
     * @param fromClass 源对象类型，如果为null则自动获取
     * @param toCreator 目标对象创建器
     * @param toClass 目标对象类型，如果为null则自动获取
     * @param <F> 源对象类型
     * @param <T> 目标对象类型
     * @return 拷贝属性后的目标对象
     */
    public static <F, T> T copyCreating(
            F from,
            Class<? extends F> fromClass,
            @NonNull Supplier<T> toCreator,
            Class<? super T> toClass
    ) {
        var to = toCreator.get();
        if (from == null || to == null) {
            return to;
        }

        copy(from, fromClass, to, toClass);
        return to;
    }

    /**
     * 创建目标对象实例并将源对象属性拷贝到其中（简化版本）
     *
     * @param from 源对象
     * @param toCreator 目标对象创建器
     * @param <T> 目标对象类型
     * @return 拷贝属性后的目标对象
     */
    public static <T> T copyCreating(Object from, @NonNull Supplier<T> toCreator) {
        return copyCreating(from, null, toCreator, null);
    }

    /**
     * 将源对象集合的元素属性批量拷贝到新创建的目标对象集合中
     *
     * @param iterable 源对象集合
     * @param fromClass 源对象类型，如果为null则自动获取
     * @param toCreator 目标对象创建器
     * @param toClass 目标对象类型，如果为null则自动获取
     * @param useSameCopier 是否使用同一个copier进行拷贝。
     *                      这意味着在列表的整个拷贝周期中，一旦生成/获取到有效的copier，则后续所有元素的拷贝都将使用此copier。
     *                      当你的列表中所有的元素都是同种类型（而非类型的子类）时，
     *                      打开此选项，会提升执行速度（因为无需重复寻找合适的copier）。
     * @param <F> 源对象类型
     * @param <T> 目标对象类型
     * @return 拷贝属性后的目标对象集合
     */
    public static <F, T> List<T> copyCreatingMulti(
            Iterable<F> iterable,
            final Class<? extends F> fromClass,
            @NonNull Supplier<? extends T> toCreator,
            final Class<? super T> toClass,
            boolean useSameCopier
    ) {
        var list = new ArrayList<T>();
        if (iterable == null) {
            return list;
        }

        var copier = fromClass == null || toClass == null ? null : getOrCreateBeanCopier(fromClass, toClass);
        for (F from : iterable) {
            var to = toCreator.get();
            if (to == null) {
                // null 对象不添加到结果列表中
                continue;
            }

            if (from != null) {
                var currentCopier = copier;
                if (currentCopier == null) {
                    var currentFromClass = fromClass;
                    if (currentFromClass == null) {
                        @SuppressWarnings("unchecked")
                        Class<F> cls = (Class<F>) from.getClass();
                        currentFromClass = cls;
                    }

                    var currentToClass = toClass;
                    if (currentToClass == null) {
                        @SuppressWarnings("unchecked")
                        Class<T> cls = (Class<T>) to.getClass();
                        currentToClass = cls;
                    }
                    currentCopier = getOrCreateBeanCopier(currentFromClass, currentToClass);

                    if (useSameCopier) {
                        copier = currentCopier;
                    }
                }

                currentCopier.copy(from, to, null);
            }

            list.add(to);
        }

        return list;
    }

    /**
     * 将源对象集合的元素属性批量拷贝到新创建的目标对象集合中（简化版本）
     * @param iterable 源对象集合
     * @param toCreator 目标对象创建器
     * @param <F> 源对象类型
     * @param <T> 目标对象类型
     * @return 拷贝属性后的目标对象集合
     */
    public static <F, T> List<T> copyCreatingMulti(Iterable<F> iterable, @NonNull Supplier<? extends T> toCreator) {
        return copyCreatingMulti(iterable, null, toCreator, null, true);
    }

    /**
     * 获取或创建BeanCopier实例
     * 
     * @param fromClass 源对象类型
     * @param toClass 目标对象类型
     * @return BeanCopier实例
     */
    private static BeanCopier getOrCreateBeanCopier(@NotNull Class<?> fromClass, @NotNull Class<?> toClass) {
        return CACHE.computeIfAbsent(
                new _CacheKey(fromClass, toClass),
                key -> BeanCopier.create(key.fromClass, key.toClass, false)
        );
    }

    /**
     * BeanCopier缓存键
     */
    private record _CacheKey(Class<?> fromClass, Class<?> toClass) {

    }
}
